home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / ocl_thr1.zip / ocl_thr1.cpp < prev    next >
Text File  |  1997-07-01  |  12KB  |  337 lines

  1. /* Stéphane Charette, stephane@venus.ubishops.ca
  2.  * IBM Open Class Library Thread & Timer Sample
  3.  * (using Visual Age C++ v3.0 with OS/2 v4.0)
  4.  * 1997June24
  5.  */
  6.  
  7. /* IBM includes necessary for OCL */
  8. #include <iapp.hpp>        // general applications calls
  9. #include <icmdevt.hpp>     // command events
  10. #include <icmdhdr.hpp>     // command events handler (pushbtn and system menu)
  11. #include <iframe.hpp>      // frame window calls
  12. #include <ilistbox.hpp>    // listbox calls
  13. #include <ipushbut.hpp>    // pushbutton calls
  14. #include <istring.hpp>     // string calls
  15. #include <isysmenu.hpp>    // system menu defines (idClose, idMinimize, etc)
  16. #include <ithread.hpp>     // thread calls
  17. #include <itimer.hpp>      // timer calls
  18.  
  19. /* local includes */
  20. #include "ocl_thr1.h"
  21.  
  22. /* function prototypes */
  23. int main( void );
  24.  
  25. /* class prototypes */
  26. class MyDlgClass;
  27. class MyThreadClass;
  28.  
  29.  
  30. /* class definition for the window dialog */
  31. class MyDlgClass :                     // inherit from...
  32.    public IFrameWindow,                // ...IFrameWindow...
  33.    public ICommandHandler              // ...and from ICommandHandler
  34. {
  35.    public:
  36.       // declare class variables
  37.       IPushButton *pbtnCreate;         // dialog pushbutton
  38.       IPushButton *pbtnDelete;         // dialog pushbutton
  39.       IPushButton *pbtnClear;          // dialog pushbutton
  40.       MyThreadClass *myThreadFn;       // used to keep track of 2nd thread
  41.       IListBox *lb;                    // listbox
  42.       // declare class methods
  43.       MyDlgClass();                    // constructor
  44.       ~MyDlgClass();                   // destructor
  45.       // declare class methods overloaded from ICommandHandler
  46.       Boolean command( ICommandEvent &event );
  47.       Boolean systemCommand( ICommandEvent &event );
  48.       // declare class method which will be called using a second thread
  49.       void printSomething();
  50. };
  51.  
  52.  
  53. /* class definition for the thread */
  54. class MyThreadClass :                  // inherit from...
  55.    public IThreadFn                    // ...IThreadFn
  56. {
  57.    public:
  58.       // declare class variables
  59.       Boolean done;                    // flag used in run() method
  60.       MyDlgClass &frame;               // points to the frame dialog window
  61.       ITimer *timer;                   // timer to call foo() every N millisecs
  62.       // declare class methods
  63.       MyThreadClass(MyDlgClass &dlg);  // constructor
  64.       ~MyThreadClass();                // destructor
  65.       void run();                      // method called when thread is started
  66. };
  67.  
  68.  
  69. // constructor for class
  70. MyThreadClass::MyThreadClass( MyDlgClass &dlg ) :
  71.    frame( dlg )
  72. {
  73.    // set the flag used in MyThreadClass::run() to
  74.    // false so that run() will keep looping forever
  75.    done = false;
  76.  
  77.    // start timer and call MyDlgClass::printSomething every 250 milliseconds
  78.       // Note: I chose 250 milliseconds since it is exactly twice
  79.       //       as fast as the pause time in MyThreadClass::run().
  80.       //       Thus, the timer messages should appear generally
  81.       //       twice as often as the run() messages.
  82.    timer = new ITimer(
  83.       new ITimerMemberFn0<MyDlgClass>(frame, MyDlgClass::printSomething), 250);
  84.  
  85.    return;
  86. }
  87.  
  88.  
  89. // destructor for class
  90. MyThreadClass::~MyThreadClass()
  91. {
  92.    // stop the timer so it can be de-allocated
  93.    timer->stop();
  94.  
  95.    // free the resources used by the timer
  96.    delete( timer );
  97.  
  98.    return;
  99. };
  100.  
  101.  
  102. void MyThreadClass::run()
  103. {
  104.    // Note: the "done" flag is initially set to false in the
  105.    //       class constructor so that the run() method keeps
  106.    //       looping potentially forever.  This is necessary
  107.    //       since the thread will cease to exist as soon as
  108.    //       run() has finished.  We use this to our advantage
  109.    //       since when it comes time to kill the thread, we
  110.    //       can set this flag to "true" from our main thread,
  111.    //       thus killing the thread.
  112.    while( ! done )
  113.    {
  114.       // sleep for 500 milliseconds (1/2 second)
  115.       IThread::current().sleep( 500 );
  116.  
  117.       // display a message in the listbox
  118.       frame.lb->addAsFirst( "print from thread..." );
  119.    }
  120.    // display a few messages as we're leaving the thread
  121.    // Note: by the time the destructor for this class has been called,
  122.    //       the ICurrentThread::terminateGUI() method seems to have
  123.    //       already been called, so you cannot send messages from
  124.    //       the destructor.
  125.    frame.lb->addAsFirst( "done flag detected..." );
  126.    frame.lb->addAsFirst( "killing timer & thread..." );
  127.  
  128.    return;
  129. }
  130.  
  131.  
  132. // constructor for class
  133. MyDlgClass::MyDlgClass() :    // we inherited from other classes so...
  134.    IFrameWindow( DLG_RCID ),  // ...call constructor for IFrameWindow
  135.    ICommandHandler()          // ...and for ICommandHandler
  136. {
  137.    // mark the thread class as being not created
  138.    myThreadFn = 0;
  139.       // when we exit the application, we will compare "myThreadFn"
  140.       // to null -- if it is equal to any other value, then we
  141.       // will assume that the second thread is running, and we'll
  142.       // kill it off before exiting
  143.  
  144.    // create the listbox
  145.    lb = new IListBox( LB_RCID, this );
  146.  
  147.    // create the pushbuttons
  148.    pbtnCreate = new IPushButton( PBTN_CREATE_RCID, this );
  149.    pbtnDelete = new IPushButton( PBTN_DELETE_RCID, this );
  150.    pbtnClear  = new IPushButton( PBTN_CLEAR_RCID,  this );
  151.  
  152.    // tell ourselves we can handle our own command events
  153.    ICommandHandler::handleEventsFor( this );
  154.       // There are two protected methods from ICommandHandler
  155.       // that we've overloaded: command() and systemCommand().
  156.       // These functions will now be called when a command or
  157.       // a system command occurs, because we've set ourselves
  158.       // up to handle our own events.  The reason we're doing
  159.       // this is because pushbuttons generate command events,
  160.       // and choosing "close" from the system menu generates
  161.       // a system event:  we want to act upon these events
  162.  
  163.    return;
  164. }
  165.  
  166.  
  167. // destructor for class
  168. MyDlgClass::~MyDlgClass()
  169. {
  170.    // stop handling command and systemCommand events
  171.    ICommandHandler::stopHandlingEventsFor( this );
  172.  
  173.    // free resources
  174.    delete( pbtnCreate );
  175.    delete( pbtnDelete );
  176.    delete( pbtnClear );
  177.    delete( lb );
  178.  
  179.    return;
  180. }
  181.  
  182.  
  183. // command event method overloaded from ICommandHandler
  184. Boolean MyDlgClass::command( ICommandEvent &event )
  185. {
  186.    switch( event.commandId() )
  187.    {
  188.       case PBTN_CREATE_RCID:
  189.       {  // the CREATE pushbutton was pressed...so start by disabling it...
  190.          pbtnCreate->disable();
  191.          // ...and create class that will run on a thread...
  192.          myThreadFn = new MyThreadClass( *this );
  193.          // ...and create the thread for the class...
  194.             // Note: using the IThread() constructor with an
  195.             //       IThreadFn-derived parameter will not only
  196.             //       create the thread, but also automatically
  197.             //       start it as if IThread::start() had been
  198.             //       called.  The TRUE parameter specified that
  199.             //       this thread needs access to PM resources
  200.             //       and should be initialized accordingly.
  201.          IThread *myThread = new IThread( myThreadFn, true );
  202.          // ...and delete the thread resource...
  203.             // Note: deleting the IThread does NOT stop the thread!
  204.             //       It will keep on running it's run() method to the
  205.             //       end, which in our case, is determined by the
  206.             //       MyThreadClass::done variable.
  207.          delete( myThread );
  208.          // ...and enable the DELETE pushbutton...
  209.          pbtnDelete->enable();
  210.          // ...and return indicating that the event has been handled
  211.          return true;
  212.          break;
  213.       }
  214.       case PBTN_DELETE_RCID:
  215.       {  // the DELETE pushbutton was pressed...so start by disabling it...
  216.          pbtnDelete->disable();
  217.          // ...and then set the flag which will allow the thread to end...
  218.             // Note: do *NOT* try and suspend the thread since it was
  219.             //       created with GUI initialization enabled.  This is
  220.             //       not documented in IThread::suspend(), but it is
  221.             //       mentionned in ICurrentThread::suspend().
  222.          myThreadFn->done = true;
  223.          // ...and keep track of the fact that we've deleted it...
  224.          myThreadFn = 0;
  225.          // ...and enable the CREATE pushbutton...
  226.          pbtnCreate->enable();
  227.          // ...and return indicating that the event has been handled
  228.          return true;
  229.          break;
  230.       }
  231.       case PBTN_CLEAR_RCID:
  232.       {  // the CLEAR pushbutton was pressed...so start by disabling it...
  233.          pbtnClear->disable();
  234.          // ...and then remove all entries in the listbox...
  235.          lb->removeAll();
  236.          // ...and return indicating that the event has been handled
  237.          return true;
  238.          break;
  239.       }
  240.    }
  241.    // return and indicate the event has not been handled
  242.    return false;
  243. }
  244.  
  245.  
  246. // system command event method overloaded from ICommandHandler
  247. Boolean MyDlgClass::systemCommand( ICommandEvent &event )
  248. {
  249.    // Note: there are three types of events we are interested in:
  250.    //       1) the minimize event
  251.    //       2) the restore event
  252.    //       3) the close event
  253.    if( event.commandId() == ISystemMenu::idClose )
  254.    {
  255.       // the user has selected to close the window, so make certain
  256.       // that the secondary thread is not running; kill it if it is
  257.       if( myThreadFn )
  258.       {
  259.          // the secondary thread exists...so we have to kill it...
  260.          myThreadFn->done = true;
  261.          myThreadFn = 0;
  262.          // and give up the rest of our time slice
  263.          // to allow the kill to take effect
  264.          IThread::current().sleep( 1 );
  265.       }
  266.    }
  267.    else if( event.commandId() == ISystemMenu::idMinimize )
  268.    {
  269.       // hide the listbox to prevent it from hiding the minized icon
  270.          // Note: because we're not using a ICanvas (or a derivitive)then
  271.          //       when we minimize the window, some of the controls may
  272.          //       overwrite the icon.  What we have to do is detect when
  273.          //       a window is minized, and hide the offending controls.
  274.          //       When using an ICanvas, this functionality is taken care
  275.          //       of for us, so we don't have to worry about it.  Also note
  276.          //       that when a window is restored, we have to remember to
  277.          //       show whatever controls have been hidden.
  278.       lb->hide();
  279.    }
  280.    else if( event.commandId() == ISystemMenu::idRestore )
  281.    {
  282.       // restore the listbox since we had to hide it...
  283.       lb->show();
  284.    }
  285.  
  286.    // return and indicate the event has not been handled
  287.    return false;
  288. }
  289.  
  290.  
  291. // print a message in the listbox
  292. void MyDlgClass::printSomething()
  293. {
  294.    static unsigned long counter = 0;   // variable used to print number
  295.  
  296.    // print the message
  297.    lb->addAsFirst( IString("print from timer: ") + IString( counter ) );
  298.  
  299.    // is the "clear" button enabled?
  300.    if( ! pbtnClear->isEnabled() )
  301.    {  // ...no, so enable it now
  302.       pbtnClear->enable();
  303.    }
  304.  
  305.    // increment the counter for next time
  306.    counter ++;
  307.  
  308.    return;
  309. }
  310.  
  311.  
  312. int main()
  313. {
  314.    // create the dialog window
  315.    MyDlgClass *frameDlg = new MyDlgClass();
  316.  
  317.    // set the icon for the window
  318.    frameDlg->setIcon( DLG_RCID );
  319.  
  320.    // add the window to the CTRL-ESC window list
  321.    frameDlg->addToWindowList();
  322.  
  323.    // enable the "create" pushbutton
  324.    frameDlg->pbtnCreate->enable();
  325.  
  326.    // run the application until it is closed
  327.    IApplication::current().run();
  328.  
  329.    // remove the window from the window list and hide it
  330.    frameDlg->removeFromWindowList().hide();
  331.  
  332.    // free up resources
  333.    delete( frameDlg );
  334.  
  335.    return 0;
  336. }
  337.